#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>   //程序替换的头文件
#include <string.h>
#include <ctype.h>    //isspace();  头文件
#include <sys/wait.h>   //wait();  头文件

int main()
{
  while(1)
  {
    //打印用户界面  和  输入的命令
    printf("[username@localhose]$ ");  //打印用户名称和所在路径
    fflush(stdout);  //因为没有'\n'来刷新缓冲区,所以使用fflush(stdout)手动刷新
    char cmd_buf[1024] = {0};   //用于存储用户输入命令的字符数组
    fgets(cmd_buf,1023,stdin);  //从标准输入读取一行字符保存到cmd_buf中
    cmd_buf[strlen(cmd_buf)-1] = '\0'; //在命令的结尾设置'\0'来表示命令结束
    printf("cmd: [%s]\n",cmd_buf);  //打印命令
    
    
    //命令解析
    int argc = 0;     //记录输入了几个命令
    char *argv[32] = {NULL};  //存储命令
    char *ptr = cmd_buf;    //用一个字符指针来判断命令是否结束
    while(*ptr != '\0')   //命令是否完全获取   
    {
      if(!isspace(*ptr)){   //isspace();  判断是否是一个空白字符
        argv[argc] = ptr;   //如果不是空白字符,将指针(ptr)保存到指针数组中,也就是一个命令的开始
        argc++;             //获取到的命令个数增加
        while(!isspace(*ptr) && *ptr != '\0')  ptr++;  //让保存命令的数组继续向后寻找,直到找到下一个空白字符
        *ptr = '\0';        //将该空白字符设成'\0',作为一个命令的结尾
      }
      argv[argc] = NULL;    //将最后一个命令设为空
      ptr++;                //是空字符,指针继续向后走,直到不是空字符为止
    }
    //int i = 0;
    //for(i = 0; i < argc; i++)
    //{
      //此处%s打印的是一个字符串,字符串结尾标志是'\0',所以上面让每一个命令的后面跟一个'\0',是为了能够区分出来
      //指针数组中保存的是命令的起始地址,将起始地址传参,%s打印,到'\0'结束
      //printf("argv[%d]=[%s]\n", i, argv[i]); 
    //}
    


    //创建一个子进程来替代父进程来执行命令,因为如果用父进程进行程序替换,会使原有的父进程不复存在
    pid_t pid = fork();   //创建一个新的进程
    if(pid < 0 ){     //pid是一个返回值,如果pid < 0;表示子进程创建失败
      continue;       //若子进程创建失败,则重新创建
    }
    //创建好子进程以后,子进程和父进程的代码一样,但是子进程会从它创建成功的地方向下执行,
    //而父进程会继续执行它的代码
    else if(pid == 0){    //pid == 0表示该进程为子进程
      //子进程进来以后进行程序替换,将原有的代码替换成我们所输入的命令来执行
      execvp(argv[0],argv); //execvp();第一个参数是一个字符串,也就是命令名称,第二个参数是一个字符类型的指针数组

      //若程序替换成功,子进程替换了命令执行完毕之后,将其退出
      //若程序替换失败,子进程将执行其父进程的代码而成为一个新的shell,可能导致出现无限递归,所以也要将其退出
      exit(0);    //进程终止
    }
    wait(NULL);   //父进程接受子进程的退出信息并将子进程的资源释放,避免子进程成为一个僵尸进程
  }


  return 0;
}

